#include "cluster.h"

using namespace std;

struct Order {
	string *cmd;
	Node *node;
};
	
void *nodeExecute(void* o) {
	Order *order = (Order*)o;
	order->node->execute(order->cmd);
};

Cluster::Cluster(string hosts, bool cached, list<string*> *output) {
	console = output;
	hostfile = hosts;
	nodes = new list<Node*>();
	threads = list<pthread_t*>();
	usecache = cached;
	findNodes();
};

void Cluster::resetNodes() {
	list<Node*>::iterator n;
	for(n = nodes->begin(); n != nodes->end(); n++) {
		(*n)->setOnline(false);
	}
};

void Cluster::setNodeOnline(string *ip, bool online) {
	list<Node*>::iterator n;
	for(n = nodes->begin(); n != nodes->end(); n++) {
		Node *node = (*n);
		if(node->getIP().compare(*ip) == 0) {
			//cout << "Node(" << node->getHostname() << ") is ";
			//cout << (online ? "ON" : "OFF") << endl;
			node->setOnline(online);
			return;
		}
	}
};

void Cluster::setNodeActive(string *ip, bool active) {
	list<Node*>::iterator n;
	for(n = nodes->begin(); n != nodes->end(); n++) {
		Node *node = (*n);
		if(node->getIP().compare(*ip) == 0) {
			//cout << "Node(" << node->getHostname() << ") is ";
			//cout << (active ? "ACTIVE" : "RELAXED") << endl;
			node->setActive(active);
			return;
		}
	}
};

bool Cluster::isNodeOnline(string *ip) {
	list<Node*>::iterator n;
	for(n = nodes->begin(); n != nodes->end(); n++) {
		Node node = *(*n);
		if(node.getIP().compare(*ip) == 0);
		return node.isOnline();
	} return false;
};

bool Cluster::isNodeActive(string *ip) {
	list<Node*>::iterator n;
	for(n = nodes->begin(); n != nodes->end(); n++) {
		Node node = *(*n);
		if(node.getIP().compare(*ip) == 0);
		return node.isActive();
	} return false;
};

void Cluster::findNodes() {
	string ip;
	string host;
	ifstream data;
	data.exceptions(ifstream::eofbit | ifstream::failbit | ifstream::badbit);
	try {
		data.open(hostfile.c_str());
		while(data >> host >> ip) {
			//cout << "Node(" << host << ") created" << endl;
			nodes->push_back(new Node(host,ip,console));
		} data.close();
	} catch (ifstream::failure& e) {
		// dont do a damn thing
	} if(usecache) {
		resetNodes(); // clears the 'online' flag
		ifstream cache;
		string ip;
		cache.exceptions(ifstream::eofbit | ifstream::failbit | ifstream::badbit);
		try {
			cache.open("nodes.cache");
			while(cache >> ip) {
				//cout << "found host " << ip << endl;
				setNodeOnline(&ip,true);
			} cache.close();
		} catch (ifstream::failure& e) {
			// dont do a damn thing
		}
	} else {
		probe();
	}
};

list<Node*> *Cluster::getOnlineNodes() {
	list<Node*> *online = new list<Node*>();
	list<Node*>::iterator n;
	if(!usecache) probe();
	for(n = nodes->begin(); n != nodes->end(); n++) {
		Node *node = (*n);
		//cout << "Node(" << node->getHostname() << ") is ";
		if(node->isOnline()) {
			//cout << "ON";
			online->push_back(node);
		} else {
			//cout << "OFF";
		} //cout << endl;
	} return online;
};

list<Node*> *Cluster::getActiveNodes() {
	list<Node*> *active = new list<Node*>();
	list<Node*>::iterator n;
	for(n = nodes->begin(); n != nodes->end(); n++) {
		Node *node = (*n);
		//cout << "Node(" << node->getHostname() << ") is ";
		if(node->isActive()) {
			//cout << "ACTIVE";
			active->push_back(node);
		} else {
			//cout << "RELAXED";
		} //cout << endl;
	} return active;
};

list<Node*> *Cluster::getNodes() {
	return nodes;
};

void Cluster::probe() {
	// assume all are off
	list<Node*>::iterator n;
	for(n = nodes->begin(); n != nodes->end(); n++) {
		(*n)->setOnline(false);
	}; // use nmap to discover online nodes
	FILE *fp;
	int status;
	char line[255];
	string nmap_regex("(192\\.168\\.[0-9]{1,3}\\.[0-9]{1,3})");
	boost::regex re;
	boost::cmatch matches;
	fp = popen("nmap -sP 192.168.50.148-170","r"); // fp = popen("nmap -sP 192.168.50.151-170","r");
	if (fp == NULL) {
		cout << "Unable to start nmap." << endl;
		return;
	} try {
		char line[255];
		re.assign(nmap_regex, boost::regex_constants::icase);
		while (fgets(line, 255, fp) != NULL) {
			if (boost::regex_search(line, matches, re)) {
				if(matches.size() > 0) {
					string *ip = new string(matches[1].first, matches[1].second);
					//cout << "found node " << *ip << endl;
					setNodeOnline(ip,true);
				}
			}
		}
	} catch (boost::regex_error& e) {
		cout << "Regex error. " << endl;
	} status = pclose(fp);
	if (status == -1) {
		cout << "Error closing file." << endl;
	}
};

void Cluster::execute(list<Node*> *nodes, string *command) {
	console->clear();
	list<Node*>::iterator n;
	list<string*>::iterator s;
	list<Order*> orders = list<Order*>();
	list<Order*>::iterator o;
	threads.clear();
	cout << "there are " << nodes->size() << " nodes." << endl;
	for(n = nodes->begin(); n != nodes->end(); n++) {
		pthread_t *new_thread = new pthread_t;
		threads.push_front(new_thread);
		Order *order = new Order;
		orders.push_back(order);
		order->cmd = command;
		order->node = *n;
		cout << "starting a thread for " << (*n)->getHostname() << endl;
		pthread_create(new_thread,NULL,nodeExecute,(void*)order);
	} for(t = threads.begin(); t != threads.end(); t++) {
		pthread_join(*(*t),NULL); // will block until all threads done
	}
};

bool Cluster::isUsingCache() {return usecache;};
bool Cluster::setUseCache(bool use) {usecache = use;};

